﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AAFLogAnalysis
{
    public partial class Form1 : Form
    {
        private string LogsStr;//日志字符串
        List<LogEntity> ErrorAnalysisResult;//日志对象集合
        IEnumerable<IGrouping<string, LogEntity>> ErrorAnalysisResultGroup = null;//日志分析集合

        List<LogEntity> AllLogAnalysisResult;
        IEnumerable<IGrouping<string, LogEntity>> AllLogAnalysisResultGroup = null;//日志分析集合

        List<LogEntity> AllAnalysisResult;
        DGV_DisplayType DGV_DisplayType = DGV_DisplayType.None;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.btn_analysis.Enabled = false;
            this.btn_showErrors.Enabled = false;
            SetLogsMenuEnabled();
            this.pl_errorAnalysis.Hide();
            this.pl_content.Hide();

            this.dgv_errorsAnalysis.RowsDefaultCellStyle.Font = new Font("微软雅黑", 14);
            this.dgv_errorsAnalysis.RowsDefaultCellStyle.ForeColor = Color.Gray;
            this.dgv_errorsAnalysis.RowTemplate.Height = 28;
            this.dgv_errorsAnalysis.CellMouseDown += dgv_right_selected_CellMouseDown;
            this.dgv_errorsAnalysis.AllowUserToOrderColumns = true;

            this.dgv_logs.RowsDefaultCellStyle.Font = new Font("微软雅黑", 14);
            this.dgv_logs.RowsDefaultCellStyle.ForeColor = Color.Gray;
            this.dgv_logs.RowTemplate.Height = 28;
            this.dgv_logs.CellMouseDown += dgv_right_selected_CellMouseDown;
            this.dgv_logs.AllowUserToOrderColumns = true;
            this.dgv_logs.DragEnter += file_DragEnter;
            this.dgv_logs.DragDrop += file_DragDrop;

            this.btn_analysis.Click += btn_analysis_Click;
            this.btn_showErrors.Click += btn_showErrors_Click;
            this.btn_showWarning.Click += btn_showWarning_Click;
            this.pl_child_btn_errorsAnalysis_close.Click += pl_child_btn_close_Click;
            this.pl_child_btn_content_colse.Click += pl_child_btn_content_colse_Click;

            this.pl_file.AllowDrop = true;
            this.pl_file.DragEnter += file_DragEnter;
            this.pl_file.DragDrop += file_DragDrop;
            this.label1.AllowDrop = true;
            this.label1.DragEnter += file_DragEnter;
            this.label1.DragDrop += file_DragDrop;
        }

        //分析
        void btn_analysis_Click(object sender, EventArgs e)
        {
            this.btn_analysis.Enabled = false;
            string path = btn_analysis.Tag.ToString();
            //path = Environment.CurrentDirectory + "\\Aaf.log";f
            if (String.IsNullOrEmpty(LogsStr))
            {
                //LogsStr = File.ReadAllText(path);
                var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                StreamReader sr = new StreamReader(fs);
                LogsStr = sr.ReadToEnd();
                sr.Dispose();
                fs.Dispose();
            }
            this.btn_showErrors.Enabled = true;
            ErrorAnalysisResult = null;
            ErrorAnalysisResultGroup = null;

            AllLogAnalysisResult = null;
            AllLogAnalysisResultGroup = null;
        }
        //错误日志
        void btn_showErrors_Click(object sender, EventArgs e)
        {
            DGV_DisplayType = AAFLogAnalysis.DGV_DisplayType.AllErrorAnalysis;
            if (LogsStr == null)
                return;
            if (ErrorAnalysisResult == null)
                ErrorAnalysisResult = ErrorAnalysis();
            if (cb_brief.Checked)
                ErrorAnalysisResultGroup = ErrorAnalysisResult.GroupBy(p => p.Brief, new LogEntityGroupForBrief());
            if (cb_calssName.Checked)
                ErrorAnalysisResultGroup = ErrorAnalysisResult.GroupBy(p => p.ClassName);
            if (cb_methodName.Checked)
                ErrorAnalysisResultGroup = ErrorAnalysisResult.GroupBy(p => p.MethodName);
            if (ErrorAnalysisResultGroup == null)
            {
                DGV_DisplayType = AAFLogAnalysis.DGV_DisplayType.AllErrorLogs;
                this.dgv_logs.DataSource = ErrorAnalysisResult.ToBindingList();
                this.dgv_logs.Columns[1].Width = this.dgv_logs.Width / 2;
                this.dgv_logs.Columns[0].Width = this.dgv_logs.Width / 8;
                this.dgv_logs.Columns[2].Width = this.dgv_logs.Width / 8;
                this.dgv_logs.Columns[3].Width = this.dgv_logs.Width / 8;
                this.dgv_logs.Columns[4].Width = this.dgv_logs.Width / 8;
                SetLogsMenuEnabled(查看本条数据ToolStripMenuItem);
            }
            else
            {
                SetLogsMenuEnabled(查看本组数据ToolStripMenuItem);
                this.dgv_logs.DataSource = ErrorAnalysisResultGroup
                    .Select(p => new { Note = p.Key, Count = p.Count() })
                    .ToList()
                    .ToBindingList();

                this.dgv_logs.Columns[0].Width = this.dgv_logs.Width - 150;
                this.dgv_logs.Columns[1].Width = 100;
            }
        }
        private List<LogEntity> ErrorAnalysis()
        {
            var g = @"(?:^~:(?<date>.*?,)(?<brief>.*?Exception,)(?<className>.*?,)(?<methodName>.*?,)(?<stack>.*))";
            Regex reg = new Regex(g, RegexOptions.Multiline);
            var matches = reg.Matches(LogsStr);
            var mm = matches.Cast<Match>().Select(p => new LogEntity
            {
                Date = p.Groups["date"].Value,
                Brief = p.Groups["brief"].Value,
                ClassName = p.Groups["className"].Value,
                MethodName = p.Groups["methodName"].Value,
                Stack = p.Groups["stack"].Value
            }).ToList();
            return mm;
        }

        private List<LogEntity> AllAnalysis()
        {
            var g = @"(?:(?<ttype>^[!~]:)(?<date>.*?,)(?<brief>.*?[Exception|Warning],)(?<className>.*?,)(?<methodName>.*?,)(?<stack>.*))";
            Regex reg = new Regex(g, RegexOptions.Multiline);
            var matches = reg.Matches(LogsStr);
            var mm = matches.Cast<Match>().Select(p => new LogEntity
            {
                TType = p.Groups["ttype"].Value,
                Date = p.Groups["date"].Value,
                Brief = p.Groups["brief"].Value,
                ClassName = p.Groups["className"].Value,
                MethodName = p.Groups["methodName"].Value,
                Stack = p.Groups["stack"].Value
            }).ToList();
            return mm;
        }

        void btn_showWarning_Click(object sender, EventArgs e)
        {
            DGV_DisplayType = AAFLogAnalysis.DGV_DisplayType.AllLog;
            if (LogsStr == null)
                return;
            if (AllLogAnalysisResult == null)
                AllLogAnalysisResult = AllAnalysis();
            if (cb_brief.Checked)
                AllLogAnalysisResultGroup = AllLogAnalysisResult.GroupBy(p => p.Brief, new LogEntityGroupForBrief());
            if (cb_calssName.Checked)
                AllLogAnalysisResultGroup = AllLogAnalysisResult.GroupBy(p => p.ClassName);
            if (cb_methodName.Checked)
                AllLogAnalysisResultGroup = AllLogAnalysisResult.GroupBy(p => p.MethodName);
            if (AllLogAnalysisResultGroup == null)
            {
                DGV_DisplayType = AAFLogAnalysis.DGV_DisplayType.AllLogAnalysis;
                this.dgv_logs.DataSource = AllLogAnalysisResult.ToBindingList();
                this.dgv_logs.Columns[1].Width = this.dgv_logs.Width / 2;
                this.dgv_logs.Columns[0].Width = this.dgv_logs.Width / 8;
                this.dgv_logs.Columns[2].Width = this.dgv_logs.Width / 8;
                this.dgv_logs.Columns[3].Width = this.dgv_logs.Width / 8;
                this.dgv_logs.Columns[4].Width = this.dgv_logs.Width / 8;
                SetLogsMenuEnabled(查看本条数据ToolStripMenuItem);
            }
            else
            {
                SetLogsMenuEnabled(查看本组数据ToolStripMenuItem);
                this.dgv_logs.DataSource = AllLogAnalysisResultGroup
                    .Select(p => new { Note = p.Key, Count = p.Count() })
                    .ToList()
                    .ToBindingList();
                this.dgv_logs.Columns[1].Width = 100;
                this.dgv_logs.Columns[0].Width = this.dgv_logs.Width - 150;
            }
        }


        #region 文件拖拽和dgv公共事件
        void dgv_right_selected_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            var dgv = sender as DataGridView;
            if (e.Button == MouseButtons.Right)
            {
                if (e.RowIndex >= 0)
                {
                    dgv.ClearSelection();
                    dgv.Rows[e.RowIndex].Selected = true;
                    dgv.CurrentCell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
                }
            }
        }

        //文件拖入
        void file_DragDrop(object sender, DragEventArgs e)
        {
            LogsStr = null;
            ErrorAnalysisResult = null;
            ErrorAnalysisResultGroup = null;
            this.btn_analysis.Enabled = false;
            this.btn_search.Enabled = false;
            string[] filePath = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            if (filePath == null || filePath.Length != 1)
            {
                MessageBox.Show("请拖拽一个文件");
                return;
            }
            this.btn_analysis.Tag = filePath[0];
            this.btn_analysis.Enabled = true;
            this.btn_search.Enabled = true;
        }

        void file_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Link;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }
        #endregion


        #region 关键字搜索
        private void btn_search_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(this.txt_key.Text) || AllLogAnalysisResult == null)
            {
                MessageBox.Show("关键字 空");
                return;
            }
            DGV_DisplayType = AAFLogAnalysis.DGV_DisplayType.AllLog;
            this.dgv_logs.DataSource = AllLogAnalysisResult
                .Where(p => p.Brief.IndexOf(this.txt_key.Text) > 0 || p.Stack.IndexOf(this.txt_key.Text) > 0)
                .ToList().ToBindingList();
            this.dgv_logs.Columns[1].Width = this.dgv_logs.Width / 2;
            this.dgv_logs.Columns[0].Width = this.dgv_logs.Width / 8;
            this.dgv_logs.Columns[2].Width = this.dgv_logs.Width / 8;
            this.dgv_logs.Columns[3].Width = this.dgv_logs.Width / 8;
            this.dgv_logs.Columns[4].Width = this.dgv_logs.Width / 8;
            SetLogsMenuEnabled(查看本条数据ToolStripMenuItem);
        }

        void SeeDataContextInLogsFiles(string key, int contextCount)
        {
            var data = ErrorAnalysisResult ?? AllLogAnalysisResult;
            this.pl_content.Location = new Point(0, 0);
            this.pl_content.Size = new Size(this.Width - 15, this.Height - 40);
            this.pl_content.Show();
            var current = data.FindIndex(p => p.Date == key);
            var start = current - contextCount > 0 ? current - contextCount : 0;
            var end = current + contextCount > data.Count ? data.Count : current + contextCount;
            StringBuilder sb = new StringBuilder();
            string format = "{0}{1}{2}{3}{4}\n\n";
            var listLogs = new List<LogEntity>();
            if (this.DGV_DisplayType == AAFLogAnalysis.DGV_DisplayType.AllErrorLogs ||
                this.DGV_DisplayType == AAFLogAnalysis.DGV_DisplayType.AllErrorAnalysis)
            {
                listLogs = data.Skip(start).Take(end - start).ToList();
            }
            else if (this.DGV_DisplayType == AAFLogAnalysis.DGV_DisplayType.AllLog ||
                this.DGV_DisplayType == AAFLogAnalysis.DGV_DisplayType.AllLogAnalysis)
            {
                listLogs = AllLogAnalysisResult.Skip(start).Take(end - start).ToList();
            }
            else
            {
                listLogs = AllAnalysisResult.Skip(start).Take(end - start).ToList();
            }
            listLogs.ForEach(item =>
           {
               sb.AppendFormat(format,
                   item.Date,
                   item.Brief,
                   item.ClassName,
                   item.MethodName,
                   item.Stack.Replace(" at", Environment.NewLine + "at").Replace(" detail", Environment.NewLine + "at")
                   );
           });

            richTextBox1.ForeColor = richTextBox1.SelectionColor = Color.Black;
            richTextBox1.SelectionFont = new Font("微软雅黑", 14);
            this.richTextBox1.Text = sb.ToString();
            Font f = new Font("Verdana", 14, FontStyle.Italic, GraphicsUnit.Point);
            var cItem = data[current];
            var rkey = cItem.Date + cItem.Brief + cItem.ClassName + cItem.MethodName;
            var rstart = richTextBox1.Find(rkey);
            if (rstart >= 0)
            {
                richTextBox1.SelectionStart = rstart;
                richTextBox1.SelectionLength = rkey.Length;
                richTextBox1.SelectionFont = f;
                richTextBox1.SelectionColor = Color.Red;
            }
            richTextBox1.Multiline = true;
        }

        #endregion

        #region 层 弹出和隐藏

        //关闭具体日志信息
        void pl_child_btn_content_colse_Click(object sender, EventArgs e)
        {
            this.pl_content.Hide();
        }
        //关闭显示分析分组后的日志
        void pl_child_btn_close_Click(object sender, EventArgs e)
        {
            this.pl_errorAnalysis.Hide();
        }
        #endregion

        #region 右键菜单
        private void SetLogsMenuEnabled(ToolStripMenuItem showItem = null)
        {
            this.查看本条数据ToolStripMenuItem.Enabled = false;
            this.查看本组数据ToolStripMenuItem.Enabled = false;
            if (showItem != null)
                showItem.Enabled = true;
        }
        private void 查看本组数据ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var row = this.dgv_logs.CurrentRow;
            if (row == null)
                return;
            if (DGV_DisplayType == AAFLogAnalysis.DGV_DisplayType.AllErrorAnalysis)
            {
                if (ErrorAnalysisResultGroup == null)
                    return;
                var key = row.Cells[0].Value.ToString();
                var group = ErrorAnalysisResultGroup.FirstOrDefault(p => p.Key == key);
                if (group == null)
                    return;
                this.pl_errorAnalysis.Location = new Point(0, 0);
                this.pl_errorAnalysis.Size = new Size(this.Width - 15, this.Height - 40);
                this.pl_errorAnalysis.Show();
                this.dgv_errorsAnalysis.DataSource = group.ToList().ToBindingList();
                this.dgv_errorsAnalysis.Columns[1].Width = this.dgv_errorsAnalysis.Width / 2;
                this.dgv_errorsAnalysis.Columns[0].Width = this.dgv_errorsAnalysis.Width / 8;
                this.dgv_errorsAnalysis.Columns[2].Width = this.dgv_errorsAnalysis.Width / 8;
                this.dgv_errorsAnalysis.Columns[3].Width = this.dgv_errorsAnalysis.Width / 8;
                this.dgv_errorsAnalysis.Columns[4].Width = this.dgv_errorsAnalysis.Width / 8;
            }
            else if (DGV_DisplayType == AAFLogAnalysis.DGV_DisplayType.AllLogAnalysis)
            {
                if (AllLogAnalysisResultGroup == null)
                    return;
                var key = row.Cells[0].Value.ToString();
                var group = AllLogAnalysisResultGroup.FirstOrDefault(p => p.Key == key);
                if (group == null)
                    return;
                this.pl_errorAnalysis.Location = new Point(0, 0);
                this.pl_errorAnalysis.Size = new Size(this.Width - 15, this.Height - 40);
                this.pl_errorAnalysis.Show();
                this.dgv_errorsAnalysis.DataSource = group.ToList().ToBindingList();
                this.dgv_errorsAnalysis.Columns[1].Width = this.dgv_errorsAnalysis.Width / 2;
                this.dgv_errorsAnalysis.Columns[0].Width = this.dgv_errorsAnalysis.Width / 8;
                this.dgv_errorsAnalysis.Columns[2].Width = this.dgv_errorsAnalysis.Width / 8;
                this.dgv_errorsAnalysis.Columns[3].Width = this.dgv_errorsAnalysis.Width / 8;
                this.dgv_errorsAnalysis.Columns[4].Width = this.dgv_errorsAnalysis.Width / 8;
                this.dgv_errorsAnalysis.Columns[5].Width = this.dgv_errorsAnalysis.Width / 8;
            }
        }

        private void 查看本条数据ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Collections.Hashtable g;
            var row = this.dgv_logs.CurrentRow;
            if (row == null)
                return;
            SeeDataContextInLogsFiles(row.Cells[0].Value.ToString(), 20);
        }

        private void 查看本条数据上下文20ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var row = this.dgv_errorsAnalysis.CurrentRow;
            if (row == null)
                return;
            SeeDataContextInLogsFiles(row.Cells[0].Value.ToString(), 20);

        }

        private void 设置ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new AnalysisSetting().ShowDialog();
        }

        #endregion

        private void btn_files_Click(object sender, EventArgs e)
        {
            AllLogAnalysisResult = new List<LogEntity>();
            var files = this.GetFiles();
            Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < files.Count; i++)
                {
                    this.Invoke(new Action(() =>
                    {
                        this.Text = (i + 1) + "/" + files.Count + "------read...";
                    }));
                    this.Files_ReadToEntitys(files[i]);
                }
                this.Invoke(new Action(() =>
                {
                    this.Text = "success.............." + AllLogAnalysisResult.Count + "条记录。";
                }));
            });
        }

        void Files_ReadToEntitys(string path)
        {
            var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            StreamReader sr = new StreamReader(fs);
            LogsStr = sr.ReadToEnd();
            sr.Dispose();
            fs.Dispose();
            this.Invoke(new Action(() =>
            {
                this.Text += "------analysis...";
            }));
            var entitys = this.AllAnalysis();
            this.Invoke(new Action(() =>
            {
                this.Text += "------addToEn...";
            }));
            AllLogAnalysisResult.AddRange(entitys);
        }

        private List<string> GetFiles()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Multiselect = true;
            ofd.DefaultExt = ".log";
            ofd.Filter = "*.log|";
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                return ofd.FileNames.ToList();
            return new List<string>();
        }
    }
    public class LogEntity
    {
        public string Date { get; set; }
        public string Brief { get; set; }
        public string ClassName { get; set; }
        public string MethodName { get; set; }
        public string Stack { get; set; }
        public string TType { get; set; }
    }
    public class LogEntityGroupForBrief : IEqualityComparer<string>
    {
        static int len = Setting.Instase.按简要分组_前N个字符; //15;
        static Regex reg = new Regex(Setting.Instase.按简要分组_正则表达式);//(@"(?<tz>JS\d+|用户名.*?\s)");
        public bool Equals(string x, string y)
        {
            if (x == y)
            {
                return true;
            }
            return getStr(x) == getStr(y);
        }

        public int GetHashCode(string obj)
        {
            return getStr(obj).GetHashCode();
        }
        string getStr(string str)
        {
            if (str.Length > len)
            {
                str = reg.Replace(str, "******");
                if (str.Length > len)
                {
                    str = str.Substring(3, len - 3);
                }
            }
            return str;
        }
    }
    public enum DGV_DisplayType
    {
        None = 0,
        AllErrorAnalysis = 1,
        AllErrorLogs = 2,
        AllWraningLogs = 4,
        AllWraningAnalysis = 16,
        AllLog = 8,
        AllLogAnalysis = 8
    }
}
